﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

namespace Nirvana
{

    [CustomEditor(typeof(QualityMaxParticlesControl))]
    public class QualityMaxParticlesControlEditor : Editor
    {
        private QualityConfig qualityConfig;
        private SerializedProperty controls;
        private ReorderableList controlList;

        private Dictionary<string, ReorderableList> matsMap = new Dictionary<string, ReorderableList>();
        private int currentLevel=1;
        private class QualityBurstsProperty
        {
            public SerializedProperty property;
            public QualityMaxParticlesControlEditor mateditor;

            public void DrawBursts(Rect rect, int index, bool isActive, bool isFocused)
            {
                this.mateditor.DrawBursts(this.property, rect, index, isActive, isFocused);
            }
            public void DrawAddBursts(ReorderableList list)
            {
                this.mateditor.DrawAddBursts(list);
            }
        }


        public override void OnInspectorGUI()
        {

            if (this.qualityConfig == null)
            {
                var qualityConfigs = QualityConfig.FindConfigs();
                if (qualityConfigs.Length == 0)
                {
                    EditorGUILayout.HelpBox("There has no quality config in the project.", MessageType.Error);
                    this.qualityConfig = null;
                }
                else
                {
                    if (qualityConfigs.Length > 1)
                    {
                        EditorGUILayout.HelpBox("There are more than one quality config.", MessageType.Warning);
                    }
                    this.qualityConfig = qualityConfigs[0];
                }
            }

            if (this.qualityConfig != null)
            {
                var levelCount = this.qualityConfig.GetLevelCount();
                var qualityMenu = new string[levelCount];
                for (int i = 0; i < levelCount; ++i)
                {
                    var level = this.qualityConfig.GetLevel(i);
                    qualityMenu[i] = level.Name;
                }

                //var currentLevel = QualityConfig.QualityLevel;
                //EditorGUI.BeginChangeCheck();
                //currentLevel = EditorGUILayout.Popup( "Set Global Quality:", currentLevel, qualityMenu);
                //if (EditorGUI.EndChangeCheck())
                //{
                //    QualityConfig.QualityLevel = currentLevel;
                //}

                EditorGUILayout.Space();
                EditorGUILayout.Space();
                EditorGUI.BeginChangeCheck();
                currentLevel = EditorGUILayout.Popup("Test Current Quality:", currentLevel, qualityMenu);
                if (EditorGUI.EndChangeCheck())
                {
                    var ctrl = target as QualityMaxParticlesControl;
                    //ctrl.SetQualityLevel(currentLevel);
                    QualityConfig.QualityLevel = currentLevel; //美术测试用
                }

                EditorGUILayout.Space();
                EditorGUILayout.Space();
            }

            // Draw the editor.
            this.serializedObject.Update();
            this.controlList.DoLayoutList();
            this.serializedObject.ApplyModifiedProperties();
        }

        private void OnEnable()
        {
            if (this.target == null)
            {
                return;
            }

            this.controls = this.serializedObject.FindProperty("controls");
            this.controlList = new ReorderableList(serializedObject, this.controls);
            this.controlList.drawHeaderCallback = OnDrawHead;
            this.controlList.elementHeightCallback = DrawElementHeight;
            this.controlList.drawElementCallback = this.DrawControl;
        }


        private void OnDrawHead(Rect rect)
        {
            Rect position = new Rect(rect.x + rect.width / 2f, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
            GUI.Label(position, "Particles Quality");
        }
        private float DrawElementHeight(int height)
        {
            SerializedProperty arrayElementAtIndex = this.controls.GetArrayElementAtIndex(height);
            SerializedProperty Bursts = arrayElementAtIndex.FindPropertyRelative("Bursts");

            return (9.5f + (int)Bursts.arraySize) * EditorGUIUtility.singleLineHeight;
        }

        private void DrawControl(Rect rect, int index, bool isActive, bool isFocused)
        {
            var element = this.controls.GetArrayElementAtIndex(index);

            var target = element.FindPropertyRelative("Target");
            var EmissionRateOverTime = element.FindPropertyRelative("EmissionRateOverTime");
            var enabledLevels = element.FindPropertyRelative("EnabledLevels");
            var bursts = element.FindPropertyRelative("Bursts");

            var newRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);

            if (this.qualityConfig == null)
            {
                var origin = GUI.color;
                GUI.color = Color.red;
                GUI.Label(newRect, "Missing quality config.");
                GUI.color = origin;
            }
            else
            {
                var rectLine = new Rect(newRect.x, newRect.y, newRect.width / 3f, EditorGUIUtility.singleLineHeight);
                GUI.Label(rectLine, "QualityLevel Enable:");

                int levelCount = this.qualityConfig.GetLevelCount();
                enabledLevels.arraySize = levelCount;

                float itemWidth = (newRect.width - rectLine.width) / levelCount;
                float startX = newRect.x + rectLine.width + 6f;

                // Draw the toggle
                for (int i = 0; i < levelCount; ++i)
                {
                    var level = this.qualityConfig.GetLevel(i);
                    GUIContent levelname = new GUIContent(level.Name);
                    var rectItem = new Rect(startX + itemWidth * i, newRect.y, itemWidth, EditorGUIUtility.singleLineHeight);

                    var levelEnabled = enabledLevels.GetArrayElementAtIndex(i);
                    levelEnabled.boolValue = GUI.Toggle(rectItem, levelEnabled.boolValue, levelname);
                }

                newRect.y += 1.5f * EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(newRect, target, new GUIContent("Max Particles:"));
                newRect.y += 1.5f * EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(newRect, EmissionRateOverTime, new GUIContent("Emission RateOverTime:"));

                // Draw Bursts array
                newRect.y += 1.5f * EditorGUIUtility.singleLineHeight;
                EditorGUI.PrefixLabel(newRect, new GUIContent("Bursts："));

                newRect.y += 1.0f * EditorGUIUtility.singleLineHeight;
                ReorderableList reorderableList = this.DrawBurstsArrayUI(bursts);
                reorderableList.DoList(newRect);
            }

        }

        private ReorderableList DrawBurstsArrayUI(SerializedProperty target)
        {
            QualityBurstsProperty materilProperty = new QualityBurstsProperty();
            materilProperty.property = target;
            materilProperty.mateditor = this;
            ReorderableList reorderableList;
            if (this.matsMap.TryGetValue(materilProperty.property.propertyPath, out reorderableList))
            {
                return reorderableList;
            }

            reorderableList = new ReorderableList(materilProperty.property.serializedObject, materilProperty.property);
            reorderableList.drawHeaderCallback = DrawBurstListHeaderCallback;
            reorderableList.elementHeight = EditorGUIUtility.singleLineHeight;
            reorderableList.onAddCallback = materilProperty.DrawAddBursts;
            reorderableList.drawElementCallback = materilProperty.DrawBursts;
            this.matsMap.Add(materilProperty.property.propertyPath, reorderableList);
            return reorderableList;
        }

        private void DrawBurstListHeaderCallback(Rect rect)
        {
            rect.width -= 15f;
            rect.width /= 5f;
            rect.x += 30f;
            EditorGUI.LabelField(rect, new GUIContent("Time"));
            rect.x += rect.width;
            EditorGUI.LabelField(rect, new GUIContent("Count"));
            rect.x += rect.width;
            EditorGUI.LabelField(rect, new GUIContent("Cycles"));
            rect.x += rect.width;
            EditorGUI.LabelField(rect, new GUIContent("Interval"));
            rect.x += rect.width;
            EditorGUI.LabelField(rect, new GUIContent("Probability"));

        }
        private void DrawAddBursts(ReorderableList list)
        {

            if (list.serializedProperty != null)
            {
                list.serializedProperty.arraySize++;
                list.index = list.serializedProperty.arraySize - 1;

                SerializedProperty arrayElementAtIndex = list.serializedProperty.GetArrayElementAtIndex(list.index);
                SerializedProperty time = arrayElementAtIndex.FindPropertyRelative("time");
                SerializedProperty mmCurve = arrayElementAtIndex.FindPropertyRelative("countCurve");
                SerializedProperty m_ConstantMax = mmCurve.FindPropertyRelative("m_ConstantMax");
                SerializedProperty cycleCount = arrayElementAtIndex.FindPropertyRelative("cycleCount");
                SerializedProperty repeatInterval = arrayElementAtIndex.FindPropertyRelative("repeatInterval");
                SerializedProperty probability = arrayElementAtIndex.FindPropertyRelative("probability");
                time.floatValue = 0f;
                cycleCount.intValue = 1;
                repeatInterval.floatValue = 0.01f;
                probability.floatValue = 1.0f;
                m_ConstantMax.floatValue = 30f;
            }
            else
            {
                ReorderableList.defaultBehaviours.DoAddButton(list);
            }
        }
        private void DrawBursts(SerializedProperty property, Rect rect, int index, bool isActive, bool isFocused)
        {
            SerializedProperty arrayElementAtIndex = property.GetArrayElementAtIndex(index);
            SerializedProperty time = arrayElementAtIndex.FindPropertyRelative("time");
            SerializedProperty count = arrayElementAtIndex.FindPropertyRelative("countCurve");


            SerializedProperty cycleCount = arrayElementAtIndex.FindPropertyRelative("cycleCount");
            SerializedProperty repeatInterval = arrayElementAtIndex.FindPropertyRelative("repeatInterval");
            SerializedProperty probability = arrayElementAtIndex.FindPropertyRelative("probability");
            //rect.width -= 60f;
            rect.width /= 5f;

            //1
            Rect r1 = new Rect(rect.x, rect.y, rect.width - 10, rect.height);
            FloatDraggable(r1, time, 1f, 15f, "n3");
            r1.x += rect.width;
            //2

            Rect r2 = new Rect(r1.x, rect.y, rect.width - 15, rect.height);
            r2 = GUIMinMaxCurveInline(r2, count, 15f);
            r2.x += rect.width;

            //3
            Rect r3 = new Rect(r2.x, rect.y, rect.width, rect.height);

            r3.width -= 13f;
            if (!cycleCount.hasMultipleDifferentValues && cycleCount.intValue == 0)
            {
                r3.x += 15f;
                r3.width -= 15f;
                EditorGUI.LabelField(r3, new GUIContent("Infinite"), EditorStyles.label);
            }
            else
            {
                IntDraggable(r3, GUIContent.none, cycleCount, 15);
            }

            r3.width += 13f;
            Rect popupRect = GetPopupRect(new Rect(r3));
            GUIMMModePopUp(popupRect, cycleCount);

            //4
            r3.x += rect.width;
            FloatDraggable(r3, repeatInterval, 1f, 15f, "n3");
            //5
            r3.x += rect.width;
            FloatDraggable(r3, probability, 1f, 15f, "n3");
            r3.x += rect.width;
        }

        private float FloatDraggable(Rect rect, SerializedProperty floatProp, float remap, float dragWidth, string formatString)
        {
            var ModuleUI = typeof(Editor).Assembly.GetType("UnityEditor.ModuleUI");
            var FloatDraggable = ModuleUI.GetMethod("FloatDraggable", new Type[] { typeof(Rect), typeof(SerializedProperty), typeof(float), typeof(float), typeof(string) });
            var f = FloatDraggable.Invoke(null, new object[] { rect, floatProp, remap, dragWidth, formatString });
            return (float)f;
        }

        private void IntDraggable(Rect rect, GUIContent guicontent, SerializedProperty floatProp, float remap)
        {
            var ModuleUI = typeof(Editor).Assembly.GetType("UnityEditor.ModuleUI");
            var IntDraggable = ModuleUI.GetMethod("IntDraggable", new Type[] { typeof(Rect), typeof(GUIContent), typeof(SerializedProperty), typeof(float) });
            IntDraggable.Invoke(null, new object[] { rect, guicontent, floatProp, remap });
        }

        private Rect GUIMinMaxCurveInline(Rect rect, SerializedProperty mmCurve, float dragWidth)
        {
            SerializedProperty m_Mode = mmCurve.FindPropertyRelative("m_Mode");
            var state = (ParticleSystemCurveMode)m_Mode.enumValueIndex;
            if (state != ParticleSystemCurveMode.TwoConstants)
            {
                EditorGUI.PropertyField(rect, mmCurve, GUIContent.none);
            }
            else
            {
                EditorGUI.BeginProperty(rect, GUIContent.none, mmCurve);
                bool stateHasMultipleDifferentValues = mmCurve.hasMultipleDifferentValues;
                if (stateHasMultipleDifferentValues)
                {
                    GUI.Label(rect, new GUIContent("-"), GUI.skin.label);
                }
                else
                {
                    //序列化对象的内部字段，通过此查看具体的字段名，比如 ParticleSystem.MinMaxCurve.Mode 是个get属性，是无法find到的。
                    //需要打印出来查看到内部字段m_Mode
                    //var prop = mmCurve.serializedObject.GetIterator();
                    //while (prop.Next(true))
                    //{
                    //    Debug.Log(prop.name);
                    //}
                    SerializedProperty m_ConstantMin = mmCurve.FindPropertyRelative("m_ConstantMin");
                    SerializedProperty m_ConstantMax = mmCurve.FindPropertyRelative("m_ConstantMax");


                    Rect kUnsignedRange = new Rect(0f, 0f, 1f, 1f);
                    Rect kSignedRange = new Rect(0f, -1f, 1f, 2f);
                    float m_RemapValue = 1f;

                    Rect rect2 = rect;
                    rect2.width = rect.width * 0.5f;
                    Rect rect3 = rect2;
                    EditorGUI.BeginChangeCheck();
                    float a2 = FloatDraggable(rect3, m_ConstantMin, m_RemapValue, dragWidth, "n0");
                    if (EditorGUI.EndChangeCheck())
                    {
                        m_ConstantMin.floatValue = Mathf.Max(a2, 0f);
                    }
                    rect3.x += rect2.width;
                    EditorGUI.BeginChangeCheck();
                    float a3 = FloatDraggable(rect3, m_ConstantMax, m_RemapValue, dragWidth, "n0");
                    if (EditorGUI.EndChangeCheck())
                    {
                        m_ConstantMax.floatValue = Mathf.Max(a3, 0f);
                    }
                }
                rect.width += 13f;
                Rect popupRect = GetPopupRect(rect);
                GUIMMCurveStateList(popupRect, mmCurve);
                EditorGUI.EndProperty();
            }

            return rect;
        }

        private Rect GetPopupRect(Rect position)
        {
            position.xMin = position.xMax - 13f;
            return position;
        }
        private GUIStyle GetParticleSystemStyles(string name)
        {
            var ParticleSystemStyles = typeof(Editor).Assembly.GetType("UnityEditor.ParticleSystemStyles");
            var getInst = ParticleSystemStyles.GetMethod("Get", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            var inst = getInst.Invoke(null, null);

            var property = ParticleSystemStyles.GetProperty(name);
            GUIStyle guiStyle = (GUIStyle)property.GetValue(inst, null);
            return guiStyle;
        }

        private void GUIMMModePopUp(Rect rect, SerializedProperty modeProp)
        {
            var ShurikenDropdown = GetParticleSystemStyles("minMaxCurveStateDropDown");
            bool isClick = EditorGUI.DropdownButton(rect, GUIContent.none, FocusType.Passive, ShurikenDropdown);
            if (isClick)
            {
                GUIContent[] array = new GUIContent[]
                {
                   new GUIContent("Infinite"),
                   new GUIContent("Count"),
                };
                GenericMenu genericMenu = new GenericMenu();
                for (int i = 0; i < array.Length; i++)
                {
                    genericMenu.AddItem(array[i], modeProp.intValue == i, new GenericMenu.MenuFunction2(SelectModeCallback), new ModeCallbackData(i, modeProp));
                }
                genericMenu.ShowAsContext();
                Event.current.Use();
            }
        }
        private void SelectModeCallback(object obj)
        {
            ModeCallbackData modeCallbackData = (ModeCallbackData)obj;
            modeCallbackData.modeProp.intValue = modeCallbackData.selectedState;

            SerializedObject serializedObject = modeCallbackData.modeProp.serializedObject;
            serializedObject.SetIsDifferentCacheDirty();
            serializedObject.ApplyModifiedProperties();
        }

        private void GUIMMCurveStateList(Rect rect, SerializedProperty minMaxCurves)
        {
            var ShurikenDropdown = GetParticleSystemStyles("minMaxCurveStateDropDown");
            bool isClick = EditorGUI.DropdownButton(rect, GUIContent.none, FocusType.Passive, ShurikenDropdown);
            if (isClick)
            {
                if (minMaxCurves != null)
                {
                    GUIContent[] array = new GUIContent[]
                    {
                        new GUIContent("Constant"),
                        new GUIContent("Curve"),
                        new GUIContent("Random Between Two Constants"),
                        new GUIContent("Random Between Two Curves"),
                    };
                    ParticleSystemCurveMode[] array2 = new ParticleSystemCurveMode[]
                    {
                        ParticleSystemCurveMode.Constant,
                        ParticleSystemCurveMode.Curve,
                        ParticleSystemCurveMode.TwoConstants,
                        ParticleSystemCurveMode.TwoCurves
                    };
                    bool[] array3 = new bool[] { true, true, true, true };

                    SerializedProperty m_Mode = minMaxCurves.FindPropertyRelative("m_Mode");
                    var state = (ParticleSystemCurveMode)m_Mode.enumValueIndex;
                    bool flag = !minMaxCurves.hasMultipleDifferentValues;
                    GenericMenu genericMenu = new GenericMenu();
                    for (int i = 0; i < array.Length; i++)
                    {
                        if (array3[i])
                        {
                            bool on = flag && state == array2[i];
                            genericMenu.AddItem(array[i], on, new GenericMenu.MenuFunction2(SelectMinMaxCurveStateCallback), new CurveStateCallbackData(array2[i], minMaxCurves));
                        }
                    }
                    genericMenu.DropDown(rect);
                    Event.current.Use();
                }

            }

        }

        private void SelectMinMaxCurveStateCallback(object obj)
        {
            CurveStateCallbackData curveStateCallbackData = (CurveStateCallbackData)obj;

            SerializedProperty m_Mode = curveStateCallbackData.minMaxCurves.FindPropertyRelative("m_Mode");
            m_Mode.enumValueIndex = (int)curveStateCallbackData.selectedState;

            SerializedObject serializedObject = m_Mode.serializedObject;
            serializedObject.SetIsDifferentCacheDirty();
            serializedObject.ApplyModifiedProperties();
        }

        private class CurveStateCallbackData
        {
            public CurveStateCallbackData(ParticleSystemCurveMode state, SerializedProperty curves)
            {
                this.minMaxCurves = curves;
                this.selectedState = state;
            }

            public SerializedProperty minMaxCurves;

            public ParticleSystemCurveMode selectedState;
        }

        private class ModeCallbackData
        {

            public ModeCallbackData(int i, SerializedProperty p)
            {
                this.modeProp = p;
                this.selectedState = i;
            }

            public SerializedProperty modeProp;

            public int selectedState;
        }







    }
}


